import torch
import numpy as np
from torch.utils.data import Dataset, DataLoader

import matplotlib.pyplot as plt
import matplotlib

# Dataset 是一个抽象类，它只有方法的声明，没有方法的实现。因此继承 Dataset 类时，至少需要对其中的 _init_(),_getitem_()和_len_()方法进行重写。
class DiabetesDataset(Dataset):
    def __init__(self, filepath):
        xy = np.loadtxt(filepath, delimiter=',', dtype=np.float32)
        self.len = xy.shape[0]
        self.x_data = torch.from_numpy(xy[:, :-1])
        self.y_data = torch.from_numpy(xy[:, [-1]])

    def __getitem__(self, item):
        return self.x_data[item], self.y_data[item]

    def __len__(self):
        return self.len

dataset = DiabetesDataset("./data/diabetes.csv.gz")
train_loader = DataLoader(dataset=dataset, batch_size=32, shuffle=True, num_workers=2)

class Model(torch.nn.Module):
    def __init__(self):
        super(Model, self).__init__()
        self.linear1 = torch.nn.Linear(8, 6)
        self.linear2 = torch.nn.Linear(6, 4)
        self.linear3 = torch.nn.Linear(4, 1)
        self.activate = torch.nn.ReLU()
        self.sigmoid = torch.nn.Sigmoid()

    def forward(self, x):
        x = self.activate(self.linear1(x))
        x = self.activate(self.linear2(x))
        x = self.activate(self.linear3(x))
        x = self.sigmoid(x)
        return x

model = Model()
# device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
# model = model.to(device)

criterion = torch.nn.BCELoss()
# criterion = criterion.to(device)
optimizer = torch.optim.Adam(model.parameters(), lr=0.01)
loss_list = []
train_step = 0
epoch_list = []


# 由于 Windows 和 Linux 在处理并行计算时调用的接口不同，因此在 Windows 操作系统中，使用 num_workers 进行多线程时会报错。
# 所以在训练时，需要对 for 循环的中的内容进行封装，可以使用一个 if 语句或函数进行封装。
if __name__ == "__main__":
    for epoch in range(10):
        for batch_idx, (inputs, labels) in enumerate(train_loader):
            # inputs = inputs.to(device)
            # labels = labels.to(device)

            y_pred = model(inputs)
            loss = criterion(y_pred, labels)
            print(epoch, loss.item())

            train_step += 1
            if train_step % 20 == 0:
                loss_list.append(loss.item())
                epoch_list.append(epoch)

            optimizer.zero_grad()
            loss.backward()
            optimizer.step()

    print(train_step)
    matplotlib.use("TkAgg")
    plt.plot(epoch_list, loss_list)
    plt.xlabel("step")
    plt.ylabel("loss")
    plt.show()